﻿using System;
using System.Text;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.LinearAlgebra.Generic;
using System.Collections.Generic;

namespace innovations.util.exts.mathdotnet
{
    public static class VectorExt
    {
        public static DenseVector PointwiseLog(this DenseVector v)
        {
            DenseVector temp = new DenseVector(v.Count);
            for (int i = 0; i < v.Count; i++)
            {
                temp[i] = Math.Log(v[i]);
            }
            return temp;
        }

        public static Vector<double> PointwiseLog(this Vector<double> v)
        {
            Vector<double> temp = new DenseVector(v.Count);
            for (int i = 0; i < v.Count; i++)
            {
                temp[i] = Math.Log(v[i]);
            }
            return temp;
        }

        public static Vector<double> PointwiseExp(this Vector<double> v)
        {
            Vector<double> temp = new DenseVector(v.Count);
            for (int i = 0; i < v.Count; i++)
            {
                temp[i] = Math.Exp(v[i]);
            }
            return temp;
        }       
        
        public static Vector<double> PointwisePow(this Vector<double> v, int degree)
        {
            Vector<double> temp = new DenseVector(v.Count);
            for (int i = 0; i < v.Count; i++)
            {
                temp[i] = Math.Pow(v[i], degree);
            }
            return temp;
        }

        public static Vector<double> PointwiseEqual(this Vector<double> v, int scalar)
        {
            Vector<double> temp = new DenseVector(v.Count);
            for (int i = 0; i < v.Count; i++)
            {
                temp[i] = Convert.ToInt32(scalar == v[i]);
            }
            return temp;
        }

        public static Vector<double> PointwiseEqual(this Vector<double> v1, Vector<double> v2)
        {
            if (v1.Count != v2.Count)
                throw new ArgumentException("v1 and v2 must be the same size");
            Vector<double> temp = new DenseVector(v1.Count);
            for (int i = 0; i < v1.Count; i++)
                temp[i] = Convert.ToInt32(v1[i] == v2[i]);
            return temp;
        }

        public static Vector<double> Insert(this Vector<double> v1, int index, double value)
        {
            if (index > v1.Count)
                throw new IndexOutOfRangeException("index not in range for vector");
            v1.SubVector(0, 2);
            Vector tempVector = new DenseVector(v1.Count + 1, 1.0);
            if (index == 0)
                v1.CopyTo(tempVector, index, index + 1, v1.Count);
            else if (index == v1.Count)
                v1.CopyTo(tempVector, 0, 0, v1.Count);
            else
            {
                v1.CopyTo(tempVector, 0, 0, index - 1);
                v1.CopyTo(tempVector, index, index + 1, (v1.Count - index));
            }

            return tempVector;
        }

        public static Vector<double> Transpose(this Vector<double> v)
        {
            return v.ToColumnMatrix().Column(0);
        }

        public static Vector<double> Delete(this Vector<double> v, int index)
        {
            if (index < 0 || index > v.Count - 1)
                throw new ArgumentOutOfRangeException("index must be in range of vector.");
            Vector<double> v1 = new DenseVector(v.Count-1);
            int counter = 0;
            for (int i = 0; i < v.Count; i++)
            {
                if (index != i)
                    v1[counter++] = v[i];
            }
            return v1;
        }

        public static Vector<double> DeleteRange(this Vector<double> v, int startIndex, int endIndex)
        {
            if (startIndex < 0 || endIndex < 0 || startIndex > v.Count - 1 || startIndex >= endIndex)
                throw new ArgumentOutOfRangeException("indices must be in range of vector.");
            Vector<double> v1 = new DenseVector(v.Count - (endIndex - startIndex + 2));
            int counter = 0;
            for (int i = startIndex; i <= endIndex; i++)
            {
                if (startIndex >= i && endIndex <= i)
                    v1[counter++] = v[i];
            }
            return v1;
        }

        public static Vector<double> Merge(this Vector<double> v, Vector<double> other)
        {
            if (v == null)
                return other;
            if (other == null)
                return v;
            List<double> values = new List<double>();
            values.AddRange(v.ToArray());
            values.AddRange(other.ToArray());
            return new DenseVector(values.ToArray());
        }

        public static string ToTable(this Vector<double> v)
        {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < v.Count; i++)
            {
                stringBuilder.Append(v[i].ToString("#.0000e-000").PadRight(12, ' ') + "\t");
                stringBuilder.Append("\n");
            }
            return stringBuilder.ToString();
        }

    }
}
